/*******************************************************************************
 * Copyright (c) 2013, Daniel Murphy
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 	* Redistributions of source code must retain the above copyright notice,
 * 	  this list of conditions and the following disclaimer.
 * 	* Redistributions in binary form must reproduce the above copyright notice,
 * 	  this list of conditions and the following disclaimer in the documentation
 * 	  and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 ******************************************************************************/
/**
 * Created at 8:05:23 AM Jan 18, 2011
 */
package org.jbox2d.testbed.perf;

import org.jbox2d.common.MathUtils;
import org.jbox2d.common.Vec2;
import org.jbox2d.pooling.IWorldPool;
import org.jbox2d.pooling.normal.DefaultWorldPool;
import org.jbox2d.profile.BasicPerformanceTest;

//Test Name               Milliseconds Avg
//Creation                         70.6609
//World Pool                      251.3508
//Circle Pool                      75.3677
//Custom Stack                     75.6705
//ThreadLocal member               77.2405
//Member                           74.3760

// Windows results 1/19/11
//Test Name               Milliseconds Avg
//Creation                         74.6291
//World Pool                      273.5585
//Circle Pool                      82.2195
//ThreadLocal member               83.2970
//Member                           80.7545

// Windows results 7/5/2011
//Test Name               Milliseconds Avg
//Creation                         79.3003
//World Pool                       82.9722
//Circle Pool                      85.9589
//Custom Stack                     85.5465
//ThreadLocal member               87.7560
//Member                           84.1358


/**
 * @author Daniel Murphy
 */
public class PoolingPerf extends BasicPerformanceTest {
  public static final int INNER_ITERS = 50000;
  public static final int OUTER_ITERS = 1000;

  public static class CirclePool {
    final Vec2[] pool;
    int index;
    final int length;

    public CirclePool() {
      pool = new Vec2[200];
      for (int i = 0; i < pool.length; i++) {
        pool[i] = new Vec2();
      }
      length = 200;
      index = -1;
    }

    public final Vec2 get() {
      index++;
      if (index >= length) {
        index = 0;
      }
      return pool[index];
    }
  }

  public static class CustStack {
    final Vec2[] pool;
    int index;

    public CustStack() {
      pool = new Vec2[50];
      for (int i = 0; i < pool.length; i++) {
        pool[i] = new Vec2();
      }
      index = 0;
    }

    public final Vec2 get() {
      return pool[index++];
    }

    public final void reduce(int i) {
      index -= i;
    }
  }

  public static class TLVec2 extends ThreadLocal<Vec2> {
    @Override
    protected Vec2 initialValue() {
      return new Vec2();
    }
  }

  public String[] tests = new String[] {"Creation", "World Pool", "Circle Pool", "Custom Stack",
      "ThreadLocal member", "Member"};

  public float aStore = 0;
  public IWorldPool wp = new DefaultWorldPool(100, 10);
  public CirclePool cp = new CirclePool();
  public TLVec2 tlv = new TLVec2();
  public Vec2 mv = new Vec2();
  public CustStack stack = new CustStack();

  public PoolingPerf() {
    super(6, OUTER_ITERS, INNER_ITERS);
  }

  public float op(Vec2 argVec) {
    argVec.set(MathUtils.randomFloat(-100, 100), MathUtils.randomFloat(-100, 100));
    argVec.mulLocal(3.2f);
    float s = argVec.length();
    argVec.normalize();
    return s;
  }

  @Override
  public void step(int argNum) {
    switch (argNum) {
      case 0:
        runCreationTest();
        break;
      case 1:
        runWorldPoolTest();
        break;
      case 2:
        runCirclePoolTest();
        break;
      case 3:
        runCustStackTest();
        break;
      case 4:
        runThreadLocalTest();
        break;
      case 5:
        runMemberTest();
        break;
    }
  }

  public void runCreationTest() {
    Vec2 v;
    float a = 0;
    for (int i = 0; i < INNER_ITERS; i++) {
      v = new Vec2();
      a += op(v);
    }
    aStore += a;
  }

  public void runWorldPoolTest() {
    Vec2 v;
    float a = 0;
    for (int i = 0; i < INNER_ITERS; i++) {
      v = wp.popVec2();
      a += op(v);
      wp.pushVec2(1);
    }
    aStore += a;
  }

  public void runCirclePoolTest() {
    Vec2 v;
    float a = 0;
    for (int i = 0; i < INNER_ITERS; i++) {
      v = cp.get();
      a += op(v);
    }
    aStore += a;
  }

  public void runThreadLocalTest() {
    Vec2 v;
    float a = 0;
    for (int i = 0; i < INNER_ITERS; i++) {
      v = tlv.get();
      a += op(v);
    }
    aStore += a;
  }

  public void runCustStackTest() {
    Vec2 v;
    float a = 0;
    for (int i = 0; i < INNER_ITERS; i++) {
      v = stack.get();
      a += op(v);
      stack.reduce(1);
    }
    aStore += a;
  }

  public void runMemberTest() {
    float a = 0;
    for (int i = 0; i < INNER_ITERS; i++) {
      a += op(mv);
    }
    aStore += a;
  }

  @Override
  public String getTestName(int argNum) {
    return tests[argNum];
  }

  public static void main(String[] c) {
    PoolingPerf p = new PoolingPerf();
    p.go();
  }
}
